//
//  HealthKitManager.swift
//  AppleStep
//
//  Created by sylar on 17/3/7.
//  Copyright © 2017年 sylar. All rights reserved.
//

import Foundation
import HealthKit
import MYModule

public class HealthKitManager {
    
    public class var sharedInstance: HealthKitManager {
        struct Singleton {
            static let instance = HealthKitManager()

            init() {}
        }
        
        return Singleton.instance
    }
    
    public let healthStore: HKHealthStore? = {
        if HKHealthStore.isHealthDataAvailable() {
            return HKHealthStore()
        } else {
            return nil
        }
    }()
    
    public let stepUnit = HKUnit.count()
    public let distanceUnit = HKUnit.meter()
    public let kcalUnit = HKUnit.kilocalorie()
    public let heartRateUnit = HKUnit.count().unitDivided(by: HKUnit.minute())
    public let o2Unit = HKUnit.percent()
    public let bpUnit = HKUnit.millimeterOfMercury()
    
    public let readSets = NSSet(array: [
        HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.stepCount) as Any, //步数
        HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.distanceWalkingRunning) as Any, //里程
        HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.activeEnergyBurned) as Any, //运动能量
        HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRate) as Any, //心率
        HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.oxygenSaturation) as Any, //血氧
        HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.bloodPressureSystolic) as Any, //血压收缩压
        HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.bloodPressureDiastolic) as Any, //血压舒张压
        HKQuantityType.categoryType(forIdentifier: .sleepAnalysis) as Any, //睡眠数据分析
        ])
    
    public let writeSets = NSSet(array: [
        HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.stepCount) as Any,
        HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.activeEnergyBurned) as Any,
        HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.distanceWalkingRunning) as Any,
        HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRate) as Any,
        HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.oxygenSaturation) as Any, //血氧
        HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.bloodPressureSystolic) as Any, //血压收缩压
        HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.bloodPressureDiastolic) as Any, //血压舒张压
        HKQuantityType.categoryType(forIdentifier: .sleepAnalysis) as Any, //睡眠数据分析
        ])
    
    public let stepsCount = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.stepCount)
    
    
    public func getStepCount(successHandle:@escaping (_ step:Int)->Void) {
        let sampleType = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.stepCount)
        let timeSortDesc = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)
        let query = HKSampleQuery(sampleType: sampleType!, predicate: self.predicateForSamplesToday(), limit: HKObjectQueryNoLimit, sortDescriptors: [timeSortDesc]) { (query, results, error) in
            if ((error) != nil) {
                print(error!)
            }
            else {
                var total = 0
                for v in results! {
                    let quantitySample = v as! HKQuantitySample
                    let quantity = quantitySample.quantity
                    let count = quantity.doubleValue(for: self.stepUnit)
                    total += Int(count)
                }
                successHandle(total)
                print("步\(total)")
            }
        }
        self.healthStore?.execute(query)
    }
    
    public func getDistance(successHandle:@escaping (_ distance:Double)->Void) {
        let distanceType = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.distanceWalkingRunning)
        let timeSortDesc = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)
        let query = HKSampleQuery(sampleType: distanceType!, predicate: self.predicateForSamplesToday(), limit: HKObjectQueryNoLimit, sortDescriptors: [timeSortDesc]) { (query, results, error) in
            if ((error) != nil) {
                print(error!)
            }
            else {
                var total = 0.0
                for v in results! {
                    let quantitySample = v as! HKQuantitySample
                    let quantity = quantitySample.quantity
                    let distance = quantity.doubleValue(for: self.distanceUnit)
                    total += distance
                }
                successHandle(total)
                print("当天行走距离\(total)")
            }
        }
        self.healthStore?.execute(query)
    }
    
    public func getCalorie(successHandle:@escaping (_ kcal:Double)->Void) {
        let distanceType = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.activeEnergyBurned)
        let timeSortDesc = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)
        let query = HKSampleQuery(sampleType: distanceType!, predicate: self.predicateForSamplesToday(), limit: HKObjectQueryNoLimit, sortDescriptors: [timeSortDesc]) { (query, results, error) in
            if ((error) != nil) {
                print(error!)
            }
            else {
                var total = 0.0
                for v in results! {
                    let quantitySample = v as! HKQuantitySample
                    let quantity = quantitySample.quantity
                    let distance = quantity.doubleValue(for: self.kcalUnit)
                    total += distance
                }
                successHandle(total/1000.0)
            }
        }
        self.healthStore?.execute(query)
    }
    
    public func predicateForSamplesToday()->NSPredicate{
        let calendar = Calendar.current
        let now = Date()
        
        var components = calendar.dateComponents([.year
            , .month, .day], from: now)
        components.hour = 0
        components.minute = 0
        components.second = 0
        
        let startDate = calendar.date(from: components)
        let endDate  = calendar.date(byAdding:  .day, value: 1, to: startDate!)
        let predicate = HKQuery.predicateForSamples(withStart: startDate, end: endDate, options: HKQueryOptions.strictEndDate)
        return predicate
    }
    
    public func update(step:Int, kcal:Int, distance:Int) {
        if HKHealthStore.isHealthDataAvailable() {
            
            let startDate = Date()
            let endDate  = Date()
            
            if step > 0 {
                
                let stepQuantity = HKQuantity.init(unit: stepUnit, doubleValue: Double(step))
                let stepType = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.stepCount)
                let stepSample = HKQuantitySample.init(type: stepType!, quantity: stepQuantity, start: startDate, end: endDate)
                
                self.healthStore?.save(stepSample, withCompletion: { (success, error) in
                    if (error == nil) {
                        log.debug("success step")
                    }
                    else {
                        log.debug(error!)
                    }
                })
                
            }
            
            if distance > 0 {
                
                let distanceQuantity = HKQuantity.init(unit: self.distanceUnit, doubleValue: Double(distance))
                let distanceType = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.distanceWalkingRunning)
                let distanceSample = HKQuantitySample.init(type: distanceType!, quantity: distanceQuantity, start: startDate, end: endDate)
                
                self.healthStore?.save(distanceSample, withCompletion: { (success, error) in
                    if (error == nil) {
                        log.debug("success distanceQuantity")
                    }
                    else {
                        log.debug(error!)
                    }
                })
                
            }
            
            if kcal > 0 {
                
                let kcalQuantity = HKQuantity.init(unit: self.kcalUnit, doubleValue: Double(kcal))
                let kcalType = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.activeEnergyBurned)
                let kcalSample = HKQuantitySample.init(type: kcalType!, quantity: kcalQuantity, start: startDate, end: endDate)
                
                self.healthStore?.save(kcalSample, withCompletion: { (success, error) in
                    if (error == nil) {
                        log.debug("success kcalQuantity")
                    }
                    else {
                        log.debug(error!)
                    }
                })
                
            }
        }
    }
    
    public func uploadHR(heartRate: Int){
        if HKHealthStore.isHealthDataAvailable() {
        
            let startDate = Date()
            let endDate  = Date()
            
            let hrQuantity = HKQuantity.init(unit: heartRateUnit, doubleValue: Double(heartRate))
            let hrType = HKQuantityType.quantityType(forIdentifier: .heartRate)
            let hrSample = HKQuantitySample.init(type: hrType!, quantity: hrQuantity, start: startDate, end: endDate)
            
            self.healthStore?.save(hrSample, withCompletion: { (success, error) in
                if (error == nil) {
                    log.debug("success hr")
                }
                else {
                    log.debug(error!)
                }
            })
        }
    }
    
    public func uploado2(o2: Int){
        if HKHealthStore.isHealthDataAvailable() {
            
            let startDate = Date()
            let endDate = Date()
            
            let o2Quanity = HKQuantity.init(unit: o2Unit, doubleValue: Double(Double(o2) / 100.0))
            let o2Type = HKQuantityType.quantityType(forIdentifier: .oxygenSaturation)
            let o2Sample = HKQuantitySample.init(type: o2Type!, quantity: o2Quanity, start: startDate, end: endDate)
            
            self.healthStore?.save(o2Sample, withCompletion: { (success, error) in
                if (error == nil) {
                    log.debug("success o2")
                }
                else {
                    log.debug(error!)
                }
            })
        }
    }
    
    public func uploadBP(sbp: Int, dbp: Int){
        if HKHealthStore.isHealthDataAvailable(){
            
            let startDate = Date()
            let endDate = Date()
            
            let sbpQuanity = HKQuantity.init(unit: bpUnit, doubleValue: Double(sbp))
            let sbpType = HKQuantityType.quantityType(forIdentifier: .bloodPressureSystolic)
            let sbpSample = HKQuantitySample.init(type: sbpType!, quantity: sbpQuanity, start: startDate, end: endDate)
            
            let dbpQuanity = HKQuantity.init(unit: bpUnit, doubleValue: Double(dbp))
            let dbpType = HKQuantityType.quantityType(forIdentifier: .bloodPressureDiastolic)
            let dbpSample = HKQuantitySample.init(type: dbpType!, quantity: dbpQuanity, start: startDate, end: endDate)
            
            let bpCorrelationType = HKCorrelationType.correlationType(forIdentifier: .bloodPressure)
            let bpCorrelation = Set<HKQuantitySample>(arrayLiteral: sbpSample,dbpSample)
            let bloopPressureSample = HKCorrelation(type: bpCorrelationType!, start: startDate, end: endDate, objects: bpCorrelation)
            
            
            
            self.healthStore?.save(bloopPressureSample, withCompletion: { (success, error) in
                if (error == nil) {
                    log.debug("success bp")
                }
                else {
                    log.debug(error!)
                }
            })
            
        }
    }
    
    public func uploadSleep(asleepTime: [Date]){
        if HKHealthStore.isHealthDataAvailable(){
            if asleepTime.count > 0 && (asleepTime.count % 2) == 0{
                retrieveSleepAnalysis(asleepTime: asleepTime)
            }
        }
    }
    
    public func retrieveSleepAnalysis(asleepTime: [Date]) {
        
        let yesterday = Date() - (60 * 60 * 24)
        let starttimeLimit: Date = getTodayZero(date: yesterday) + (60*60*20)
        let endTimeLimit = starttimeLimit + (60 * 60 * 24)
        
        if let sleepType = HKObjectType.categoryType(forIdentifier: HKCategoryTypeIdentifier.sleepAnalysis) {
            
            let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)
            let appBundleIdentifier = Bundle.main.bundleIdentifier
            let predicate = HKQuery.predicateForSamples(withStart: starttimeLimit, end: endTimeLimit, options: HKQueryOptions.strictStartDate)
            
            let query = HKSampleQuery(sampleType: sleepType, predicate: predicate, limit: HKObjectQueryNoLimit, sortDescriptors: [sortDescriptor]) { (query, tmpResult, error) -> Void in
                if error != nil {
                    return
                }
                if let result = tmpResult {
                    for item in result {
                        if let sample = item as? HKCategorySample {
                            
                            if sample.value == HKCategoryValueSleepAnalysis.asleep.rawValue{
                                let sampleBundleIdentifier = sample.sourceRevision.source.bundleIdentifier
                                
                                if appBundleIdentifier == sampleBundleIdentifier
                                {
                                    self.healthStore?.delete(sample, withCompletion: { (success, error) in
                                        if (error == nil) {
                                            log.debug("delete asleep data success")
                                        }
                                        else {
                                            log.debug(error!)
                                        }
                                    })
                                    
                                }
                            }
                        }
                    }
                }
                for i in 0..<asleepTime.count / 2{
                    if asleepTime.count > (i * 2 + 1){
                        let startTime = asleepTime[i * 2]
                        let endTime = asleepTime[i * 2 + 1]
                        log.debug("start = \(startTime.timeIntervalSince1970), end = \(endTime.timeIntervalSince1970)")
                        if endTime > startTime {
                            if let sleepType = HKObjectType.categoryType(forIdentifier: .sleepAnalysis){
                                let asleepSample = HKCategorySample(type: sleepType, value: HKCategoryValueSleepAnalysis.asleep.rawValue, start: startTime, end: endTime)
                                self.healthStore?.save(asleepSample, withCompletion: { (success, error) in
                                    if (error == nil) {
                                        log.debug("uploadSleep success")
                                    }
                                    else {
                                        log.debug(error!)
                                    }
                                })
                            }
                            
                        }
                    }
                }
            }
            
            self.healthStore?.execute(query)
        }
        
    }

    public init() {}

    
}

extension HealthKitManager {
    
    public func getTodayZero(date: Date) -> Date{
        var calendar = Calendar.init(identifier: .gregorian)
        calendar.locale = myLocal
        let components = calendar.dateComponents([.year,.month,.day], from: date)
        return (calendar.date(from: components))!
    }
    
}
